//=======================================================================//
//= Include files.													    =//
//=======================================================================//
#include "Encoder.h"
#include <wx/colour.h>

//=======================================================================//
//= Global variable define.											    =//
//=======================================================================//
const wxString ScanOrderItems[] =
{
	wxT("Horizontal First"),
	wxT("Vertical First")
};

const wxString DataDirectionItems[] =
{
	wxT("Horizontal Data"),
	wxT("Vertical Data")
};

const wxString BitOrderItems[] =
{
	wxT("Low-bit Start"),
	wxT("High-bit Start")
};

const size_t SCAN_ORDER_ITEMS_COUNT = sizeof(ScanOrderItems)/sizeof(wxString);
const size_t DATA_DIRECTION_ITEMS_COUNT = sizeof(DataDirectionItems)/sizeof(wxString);
const size_t BIT_ORDER_ITEMS_COUNT = sizeof(BitOrderItems)/sizeof(wxString);

//=======================================================================//
//= Function define.										            =//
//=======================================================================//
EncoderConfiguration::EncoderConfiguration(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_eDataDirection = VERTICAL_DATA;
	m_eScanOrder = HORIZONTAL_FIRST;
	m_eBitOrder = LOW_BIT_START;
}

EncoderConfiguration::EncoderConfiguration(const wxXmlNode* pclsXmlNode)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	(void)FromXmlNode(pclsXmlNode);
}

EncoderConfiguration::EncoderConfiguration(const EncoderConfiguration& clsSource)
{
	SetDataDirection(clsSource.GetDataDirection());
	SetScanOrder(clsSource.GetScanOrder());
	SetBitOrder(clsSource.GetBitOrder());
}

wxXmlNode* EncoderConfiguration::CreateXmlNode(void)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxXmlNode*				pclsNewNode = new wxXmlNode(wxXML_ELEMENT_NODE, CFG_XML_NODE_NAME_ENCORDER);
	wxXmlNode*				pclsNewChildNode;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(nullptr != pclsNewNode)
	{
		/* ScanOrder node. */
		pclsNewChildNode = new wxXmlNode(pclsNewNode, wxXML_ELEMENT_NODE, CFG_XML_NODE_NAME_ENC_SCAN);
		if(nullptr != pclsNewChildNode)
		{
			pclsNewChildNode->AddAttribute(CFG_XML_NODE_ATTR_VALUE, wxString::Format(wxT("%d"), m_eScanOrder));
		}
		/* DataDirection node. */
		pclsNewChildNode = new wxXmlNode(pclsNewNode, wxXML_ELEMENT_NODE, CFG_XML_NODE_NAME_ENC_DATA_DIR);
		if(nullptr != pclsNewChildNode)
		{
			pclsNewChildNode->AddAttribute(CFG_XML_NODE_ATTR_VALUE, wxString::Format(wxT("%d"), m_eDataDirection));
		}
		/* BitOrder node. */
		pclsNewChildNode = new wxXmlNode(pclsNewNode, wxXML_ELEMENT_NODE, CFG_XML_NODE_NAME_ENC_BIT_ORDER);
		if(nullptr != pclsNewChildNode)
		{
			pclsNewChildNode->AddAttribute(CFG_XML_NODE_ATTR_VALUE, wxString::Format(wxT("%d"), m_eScanOrder));
		}
	}
	return pclsNewNode;
}

bool EncoderConfiguration::FromXmlNode(const wxXmlNode* pclsXmlNode)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	wxXmlNode*				pclsChildNode;
	wxString				strReadContent;
	bool					bReturn = true;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(nullptr != pclsXmlNode)
	{
		if(pclsXmlNode->GetName().IsSameAs(CFG_XML_NODE_NAME_ENCORDER, false))
		{
			/* Start to analyze node. */
			pclsChildNode = pclsXmlNode->GetChildren();
			if(nullptr != pclsChildNode)
			{
				while(nullptr != pclsChildNode)
				{

					int iConvertValue;
					strReadContent = pclsChildNode->GetAttribute(CFG_XML_NODE_ATTR_VALUE);
					iConvertValue = _wtoi(strReadContent);
					if(iConvertValue < 0)
					{
						/* Pixel unit width cannot less then 1. */
						iConvertValue = 0;
					}
					if(pclsChildNode->GetName().IsSameAs(CFG_XML_NODE_NAME_ENC_SCAN, false))
					{
						m_eScanOrder = SCAN_ORDER(iConvertValue);
					}
					else if(pclsChildNode->GetName().IsSameAs(CFG_XML_NODE_NAME_ENC_DATA_DIR, false))
					{
						m_eDataDirection = DATA_DIRECTION(iConvertValue);
					}
					else if(pclsChildNode->GetName().IsSameAs(CFG_XML_NODE_NAME_ENC_BIT_ORDER, false))
					{
						m_eBitOrder = BIT_ORDER(iConvertValue);
					}
					pclsChildNode = pclsChildNode->GetNext();
				}
			}
		}
	}
	return bReturn;
}

EncoderConfiguration& EncoderConfiguration::operator=(const EncoderConfiguration& clsObject)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_eDataDirection = clsObject.GetDataDirection();
	m_eScanOrder = clsObject.GetScanOrder();
	m_eBitOrder = clsObject.GetBitOrder();

	return *this;
}

RasterizedMonoImage::RasterizedMonoImage(void)
{
    m_iImageWidth = 0;
    m_iImageHeight = 0;
}

RasterizedMonoImage::RasterizedMonoImage(const RasterizedMonoData& clsData)
{
    m_clsData = clsData;
    m_iImageWidth = 0;
    m_iImageHeight = 0;
}

RasterizedMonoImage::RasterizedMonoImage(const RasterizedMonoImage& clsSource)
{
    m_clsData = clsSource.Data();
    m_uiID = clsSource.m_uiID;
    m_iImageWidth = clsSource.m_iImageWidth;
    m_iImageHeight = clsSource.m_iImageHeight;
}

void RasterizedMonoImage::SetData(const RasterizedMonoData& clsNewData, unsigned int uiDataID)
{
    m_clsData = clsNewData;
    m_uiID = uiDataID;
}

void RasterizedMonoImage::Clear(void)
{
	m_clsData.clear();
	m_clsData.resize(0);
}

void RasterizedMonoImage::SetImageSize(int iImageWidth, int iImageHeight)
{
    m_iImageWidth = iImageWidth;
    m_iImageHeight = iImageHeight;
}

void RasterizedMonoImage::SetID(unsigned int uiID)
{
    m_uiID = uiID;
}

unsigned int RasterizedMonoImage::GetID(void) const
{
    return m_uiID;
}

Encoder::Encoder(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
    _initialize();
}

Encoder::~Encoder(void)
{

}

void Encoder::_initialize(void)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
    m_clsEncodeConfig.SetBitOrder(LOW_BIT_START);
    m_clsEncodeConfig.SetDataDirection(VERTICAL_DATA);
    m_clsEncodeConfig.SetScanOrder(HORIZONTAL_FIRST);
}

uint8_t Encoder::_getByte(const wxImage& clsImage, int iLineIdx, int iColumnIdx, DATA_DIRECTION eByteDriect, BIT_ORDER eNewStartBitType)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	uint8_t						ucData = 0x00;
	int							iPosX, iPosY;
	wxColour					clsPixelColour;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	if(VERTICAL_DATA == eByteDriect)
	{
		iPosX = iColumnIdx;
		iPosY = iLineIdx*8;

		if(HIGH_BIT_START == eNewStartBitType)
		{
            for(int iIdx=0; iIdx<8; iIdx++)
			{
				ucData <<= 1;
				if(1 == _getPixelValue(clsImage, iPosX, iPosY))
				{
					ucData |= 0x01;
				}
				iPosY++;
			}
		}
		else
		{
			for(int iIdx=0; iIdx<8; iIdx++)
			{
				ucData >>= 1;
				if(1 == _getPixelValue(clsImage, iPosX, iPosY))
				{
					ucData |= 0x80;
				}
				iPosY++;
			}
		}
	}
	else
	{
		iPosX = iColumnIdx*8;
		iPosY = iLineIdx;

		if(HIGH_BIT_START == eNewStartBitType)
		{
            for(int iIdx=0; iIdx<8; iIdx++)
			{
				ucData <<= 1;
				if(1 == _getPixelValue(clsImage, iPosX, iPosY))
				{
					ucData |= 0x01;
				}
				iPosX++;
			}
		}
		else
		{
			for(int iIdx=0; iIdx<8; iIdx++)
			{
				ucData >>= 1;
				if(1 == _getPixelValue(clsImage, iPosX, iPosY))
				{
					ucData |= 0x80;
				}
				iPosX++;
			}
		}
	}

	return ucData;
}

int Encoder::_getPixelValue(const wxImage& clsImage, int iCoodX, int iCoodY)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	int					iPixelValue;

    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
    if((iCoodX < clsImage.GetWidth()) && (iCoodY < clsImage.GetHeight()))
	{
		wxColour clsPixelColour(clsImage.GetRed(iCoodX, iCoodY), clsImage.GetGreen(iCoodX, iCoodY), clsImage.GetBlue(iCoodX, iCoodY));
		if((*wxBLACK) == clsPixelColour)
		{
			iPixelValue = 1;
		}
		else
		{
			iPixelValue = 0;
		}
	}
	else
	{
		iPixelValue = 0;
	}
	return iPixelValue;
}

bool Encoder::Encode(const wxImage& clsImage, RasterizedMonoImage& clsRasterizedData)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
    bool					bReturn;
    int						iWidth, iHeight;
    int						iLineNum, iColumnNum;
	uint8_t					uiData;
	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
    bReturn =				true;

    /*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	iWidth = clsImage.GetWidth();
	iHeight = clsImage.GetHeight();
	if((0 == iHeight) || (0 == iWidth))
	{
		//No data need to encode.
		bReturn = false;
	}
	else
	{
		// Byte is vertical.
		if(VERTICAL_DATA == m_clsEncodeConfig.GetDataDirection())
		{
			iLineNum = (((iHeight-1)/8)+1);
			iColumnNum  = iWidth;
		}
		// Byte is horizontal.
		else
		{
			iLineNum = iHeight ;
			iColumnNum = (((iWidth-1)/8)+1);
		}

		// Save image size.
		clsRasterizedData.SetImageSize(iWidth, iHeight);

		// Line-first
		if(HORIZONTAL_FIRST == m_clsEncodeConfig.GetScanOrder())
		{
			for(int iLineIdx=0; iLineIdx<iLineNum; iLineIdx++)
			{
				for(int iColumnIdx=0; iColumnIdx<iColumnNum; iColumnIdx++)
				{
					uiData = _getByte(clsImage, iLineIdx, iColumnIdx, m_clsEncodeConfig.GetDataDirection(), m_clsEncodeConfig.GetBitOrder());
					clsRasterizedData.Append(uiData);
				}
			}
		}
		// Column first
		else
		{
			for(int iColumnIdx=0; iColumnIdx<iColumnNum; iColumnIdx++)
			{
				for(int iLineIdx=0; iLineIdx<iLineNum; iLineIdx++)
				{
					uiData = _getByte(clsImage, iLineIdx, iColumnIdx, m_clsEncodeConfig.GetDataDirection(), m_clsEncodeConfig.GetBitOrder());
					clsRasterizedData.Append(uiData);
				}
			}
		}
	}

    return bReturn;
}

RasterizedMonoChar::RasterizedMonoChar(size_t sIndex, unsigned int uiCode)
: RasterizedMonoImage()
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_uiID = uiCode;
	m_sIndex = sIndex;
}

RasterizedMonoChar::RasterizedMonoChar(unsigned int uiCode)
: RasterizedMonoImage()
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_uiID = uiCode;
	m_sIndex = (-1UL);
}

RasterizedMonoChar::RasterizedMonoChar(const RasterizedMonoChar& clsSource)
: RasterizedMonoImage(clsSource.Data())
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_uiID = clsSource.m_uiID;
	m_sIndex = clsSource.m_sIndex;
}

void RasterizedMonoChar::SetChar(unsigned int uiCode)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_uiID = uiCode;
}

void RasterizedMonoChar::SetIndex(size_t sIndex)
{
	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/
	m_sIndex = sIndex;
}
